﻿using System;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Windows.Forms.Design;
using DevExpress.Utils.Extensions;
using XCI.Core;
using XCI.Extensions;
using XCI.Helper;

namespace XCI.Windows
{
    /// <summary>
    /// 分页控件
    /// <para>使用方法：</para>
    /// <para>1、拖拽分页控件到界面。</para>
    /// <para>2、绑定控件<see cref="PageChanged"/>事件，在此事件中进行数据加载，完成后调用控件的<see cref="SetTotal"/>方法来更新状态和消息。</para>
    /// <para>3、调用控件<see cref="SetPage"/>方法，指定要加载的页数，默认加载第一页</para>
    /// <para>4、设置<see cref="PageSize"/>属性，可以指定每页加载的记录条数，默认值由<see cref="DefaultPageSize"/>属性控制，可在系统启动时修改此静态属性，实现全局修改分页大小默认值。</para>
    /// </summary>
    [System.ComponentModel.Designer(typeof(PaginationDesigner))]
    public partial class Pagination : BaseUserControl
    {
        private static readonly Regex PageMessageParamRegex = new Regex(@"\{(.*?)\}", RegexOptions.Compiled);
        private readonly MutexStatus _pageSizeMutexStatus = new MutexStatus();
        private PageInfo currentPageInfo;
        private int currentPageIndex = 1;

        /// <summary>
        /// 初始化分页控件
        /// </summary>
        public Pagination()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 分页控件默认分页大小,默认值:20
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static int DefaultPageSize { get; set; } = 20;

        /// <summary>
        /// 分页控件默认分页大小下拉列表(多个之间逗号隔开),默认值:20,50,100,200,500
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static string DefaultPageSizeList { get; set; } = "20,50,100,200,500";

        /// <summary>
        /// 分页控件刷新按钮默认延迟时间(毫秒),默认值:5000即5秒
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static int DefaultRefreshDelay { get; set; } = 5000;

        /// <summary>
        /// 分页控件默认消息模板,默认值:显示第{StartIndex}到{EndIndex}条,共{PageIndex}/{TotalPage}页{Total}条
        /// <para>变量使用大括号包裹,<see cref="PageInfo"/>类的所有属性都可以作为变量使用</para>
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static string DefaultPageMessage { get; set; } =
            "显示第{StartIndex}到{EndIndex}条,第{PageIndex}/{TotalPage}页共{Total}条";

        /// <summary>
        /// 分页控件默认空数据消息模板,默认值:暂无数据
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static string DefaultEmptyPageMessage { get; set; } = "暂无数据";

        /// <summary>
        /// 分页大小下拉列表,空表示使用全局默认值,全局默认值使用<see cref="Pagination.DefaultPageSizeList"/>
        /// </summary>
        [Category("XCI"), Description("分页大小下拉列表,空表示使用全局默认值,全局默认值使用Pagination.DefaultPageSizeList"), DefaultValue(null)]
        public string PageSizeList { get; set; }

        /// <summary>
        /// 刷新按钮延迟时间(毫秒),-1表示使用全局默认值,全局默认值使用<see cref="Pagination.DefaultRefreshDelay"/>
        /// </summary>
        [Category("XCI"), DefaultValue(-1),
         Description("刷新按钮延迟时间(毫秒),-1表示使用全局默认值,全局默认值使用Pagination.DefaultRefreshDelay"),]
        public int RefreshDelay { get; set; } = -1;

        /// <summary>
        /// 分页控件消息模板,空表示使用全局默认值,全局默认值使用<see cref="DefaultPageMessage"/>
        /// <para>变量使用大括号包裹,<see cref="PageInfo"/>类的所有属性都可以作为变量使用</para>
        /// </summary>
        [Category("XCI"), Description("分页控件消息模板,空表示使用全局默认值,全局默认值使用DefaultPageMessage"), DefaultValue(null)]
        public string PageMessage { get; set; }

        /// <summary>
        /// 分页控件空数据消息模板,空表示使用全局默认值,全局默认值使用<see cref="DefaultEmptyPageMessage"/>
        /// </summary>
        [Category("XCI"), Description("分页控件空数据消息模板,空表示使用全局默认值,全局默认值使用DefaultEmptyPageMessage"), DefaultValue(null)]
        public string EmptyPageMessage { get; set; }

        /// <summary>
        /// 是否显示顶部的分割线
        /// </summary>
        [Category("XCI"), Description("是否显示顶部的分割线"), DefaultValue(false)]
        public bool ShowTopLine { get; set; } = false;

        /// <summary>
        /// 是否显示分页大小下拉框
        /// </summary>
        [Category("XCI"), Description("是否显示分页大小下拉框"), DefaultValue(true)]
        public bool ShowPageSizeBox { get; set; } = true;

        /// <summary>
        /// 是否显示分页信息
        /// </summary>
        [Category("XCI"), Description("是否显示分页信息"), DefaultValue(true)]
        public bool ShowPageMessage { get; set; } = true;

        /// <summary>
        /// 是否显示当前页码文本框
        /// </summary>
        [Category("XCI"), Description("是否显示当前页码文本框"), DefaultValue(true)]
        public bool ShowPageIndexBox { get; set; } = true;

        /// <summary>
        /// 是否显示首页导航按钮
        /// </summary>
        [Category("XCI"), Description("是否显示首页导航按钮"), DefaultValue(true)]
        public bool ShowFirstPageButton { get; set; } = true;

        /// <summary>
        /// 是否显示末页导航按钮
        /// </summary>
        [Category("XCI"), Description("是否显示末页导航按钮"), DefaultValue(true)]
        public bool ShowLastPageButton { get; set; } = true;

        /// <summary>
        /// 是否显示下一页导航按钮
        /// </summary>
        [Category("XCI"), Description("是否显示下一页导航按钮"), DefaultValue(true)]
        public bool ShowPreviousPageButton { get; set; } = true;

        /// <summary>
        /// 是否显示上一页导航按钮
        /// </summary>
        [Category("XCI"), Description("是否显示上一页导航按钮"), DefaultValue(true)]
        public bool ShowNextPageButton { get; set; } = true;

        /// <summary>
        /// 是否显示刷新按钮
        /// </summary>
        [Category("XCI"), Description("是否显示刷新按钮"), DefaultValue(true)]
        public bool ShowRefreshButton { get; set; } = true;

        /// <summary>
        /// 分页大小,默认值是-1,-1表示使用全局默认值,控件静态变量<see cref="DefaultPageSize"/>可以统一设置控件分页大小全局默认值
        /// <para>分页大小修改完成后，调用<see cref="SetPage"/>方法重新加载数据</para>
        /// </summary>
        [Category("XCI"), Description("分页大小,默认值是-1,-1表示使用全局默认值,全局默认值使用DefaultPageSize"), DefaultValue(-1)]
        public int PageSize { get; set; } = -1;

        /// <summary>
        /// 当前分页信息对象
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public PageInfo CurrentPageInfo => currentPageInfo;

        /// <summary>
        /// 分页改变事件
        /// </summary>
        [Category("XCI"), Description("分页改变事件")]
        public event EventHandler<PageChangedEventArgs> PageChanged;

        /// <summary>
        /// 触发分页改变事件
        /// </summary>
        /// <param name="args">事件参数</param>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected virtual void OnPageChanged(PageChangedEventArgs args)
        {
            PageChanged?.Invoke(this, args);
        }

        /// <inheritdoc />
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            //确保初始化函数只被执行一次
            OnceAction.Run("InitUserControl", InitUserControl);
        }

        /// <summary>
        /// 初始化分页控件
        /// </summary>
        private void InitUserControl()
        {
            if (WinFormHelper.IsDesignMode()) return;

            #region 初始化成员变量

            if (PageSize == -1) PageSize = DefaultPageSize;
            if (RefreshDelay == -1) RefreshDelay = DefaultRefreshDelay;
            if (string.IsNullOrEmpty(PageSizeList)) PageSizeList = DefaultPageSizeList;
            if (string.IsNullOrEmpty(PageMessage)) PageMessage = DefaultPageMessage;
            if (string.IsNullOrEmpty(EmptyPageMessage)) EmptyPageMessage = DefaultEmptyPageMessage;

            #endregion

            #region 填充分页大小下拉列表

            var pageSizeListArray = PageSizeList.SplitToArray();
            editPageSize.Properties.Items.BeginUpdate();
            editPageSize.Properties.Items.Clear();
            pageSizeListArray.ForEach(p => editPageSize.Properties.Items.Add(p));
            editPageSize.Properties.Items.EndUpdate();

            #endregion

            #region 绑定控件事件

            this.editPageSize.EditValueChanged += (o, e) =>
            {
                _pageSizeMutexStatus.End(() =>
                {
                    var psize = editPageSize.EditValue.ToInt(20);
                    this.PageSize = psize;
                    //修复当前页码,防止超过范围
                    SetPage(RepairPageIndex(currentPageIndex, psize));
                });
            };
            this.editPageIndex.KeyPress += (o, e) =>
            {
                if (e.KeyChar == R.EnterKey)
                {
                    //修复当前页码,防止超过范围
                    SetPage(RepairPageIndex(editPageIndex.EditValue.ToInt(), PageSize));
                    editPageIndex.SelectAll();
                    e.Handled = true;
                }

                if (e.KeyChar == 45) //禁止输入负数
                {
                    e.Handled = true;
                }
            };
            this.btnFirstPage.Click += (o, e) => SetPage(1);
            this.btnPreviousPage.Click += (o, e) => SetPage(currentPageIndex - 1);
            this.btnNextPage.Click += (o, e) => SetPage(currentPageIndex + 1);
            this.btnLastPage.Click += (o, e) => { SetPage(currentPageInfo.TotalPage); };
            this.btnRefresh.Click += (o, e) =>
            {
                btnRefresh.Enabled = false;
                RefreshPage();
            };

            #endregion

            SetTotal(0);
        }

        /// <summary>
        /// 跳转到指定页数(触发<see cref="PageChanged"/>事件),页数必须大于等于1，否则抛出<see cref="ArgumentException"/>异常。
        /// </summary>
        /// <param name="pageIndex">待跳转的页码,如果为空，则刷新当前页</param>
        public void SetPage(int pageIndex)
        {
            if (DesignMode) return;
            if (pageIndex <= 0) throw new ArgumentException("请指定有效的页数，页数不能小于1", nameof(pageIndex));
            currentPageIndex = pageIndex;

            OnPageChanged(GetCurrentPageChangedEventArgs());
        }

        /// <summary>
        /// 刷新当前页
        /// </summary>
        public void RefreshPage()
        {
            if (DesignMode) return;
            OnPageChanged(GetCurrentPageChangedEventArgs());
        }

        /// <summary>
        /// 更新分页状态及消息
        /// </summary>
        /// <param name="total">总记录数</param>
        public void SetTotal(int total)
        {
            currentPageInfo = new PageInfo(currentPageIndex, PageSize, total);
            //重新修正页码值,确保值正确
            currentPageIndex = currentPageInfo.PageIndex;

            editPageIndex.Properties.MaxValue = currentPageInfo.TotalPage;
            editPageIndex.EditValue = currentPageInfo.PageIndex;

            _pageSizeMutexStatus.Begin(() =>
            {
                var pageSizeString = currentPageInfo.PageSize.ToString();
                //判断PageSize下拉列表中是否包含当前PageSize,如果不包含则添加到最前面
                if (!editPageSize.Properties.Items.Contains(pageSizeString))
                {
                    editPageSize.Properties.Items.Insert(0, pageSizeString);
                }

                editPageSize.EditValue = pageSizeString;
            });

            #region 设置元素是否可用

            editPageSize.Enabled = currentPageInfo.Has;
            labDi.Enabled = labYe.Enabled = currentPageInfo.Has;
            editPageIndex.Enabled = currentPageInfo.Has;

            btnFirstPage.Enabled = currentPageInfo.Has && !currentPageInfo.IsFirstPage;
            btnLastPage.Enabled = currentPageInfo.Has && !currentPageInfo.IsLastPage;
            btnPreviousPage.Enabled = currentPageInfo.Has && currentPageInfo.HasPreviousPage;
            btnNextPage.Enabled = currentPageInfo.Has && currentPageInfo.HasNextPage;

            btnRefresh.Enabled = false;
            TimerHelper.DelayRun(RefreshDelay, () => { btnRefresh.Enabled = currentPageInfo.Has; });

            // labPageMessage.Enabled = currentPageInfo.Has;

            #endregion

            #region 设置元素是否可见

            ItemPageSize.Visibility = WinFormHelper.LayoutItemVisible(ShowPageSizeBox);

            var refreshVisibility = WinFormHelper.LayoutItemVisible(ShowRefreshButton);
            ItemRefresh.Visibility = refreshVisibility;
            ItemLineRefresh.Visibility = refreshVisibility;

            var pageIndexVisibility = WinFormHelper.LayoutItemVisible(ShowPageIndexBox);
            ItemLineDi.Visibility = pageIndexVisibility;
            ItemDi.Visibility = pageIndexVisibility;
            ItemPageIndex.Visibility = pageIndexVisibility;
            ItemYe.Visibility = pageIndexVisibility;
            ItemLineYe.Visibility = pageIndexVisibility;

            ItemFirstPage.Visibility = WinFormHelper.LayoutItemVisible(ShowFirstPageButton);
            ItemPreviousPage.Visibility = WinFormHelper.LayoutItemVisible(ShowPreviousPageButton);
            ItemNextPage.Visibility = WinFormHelper.LayoutItemVisible(ShowNextPageButton);
            ItemLastPage.Visibility = WinFormHelper.LayoutItemVisible(ShowLastPageButton);

            labPageMessage.Visibility = WinFormHelper.LayoutItemVisible(ShowPageMessage);
            separatorControl1.Visible = ShowTopLine;

            #endregion

            #region 设置消息

            var msgTpl = currentPageInfo.Has ? PageMessage : EmptyPageMessage;
            labPageMessage.Text = ResolvingPageMessage(msgTpl, currentPageInfo);

            #endregion
        }

        /// <summary>
        /// 获取当前分页事件参数对象
        /// </summary>
        protected PageChangedEventArgs GetCurrentPageChangedEventArgs()
        {
            return new PageChangedEventArgs(currentPageIndex, PageSize);
        }

        /// <summary>
        /// 解析模板消息
        /// </summary>
        /// <param name="tpl">模板</param>
        /// <param name="pageInfo">分页信息对象</param>
        private string ResolvingPageMessage(string tpl, PageInfo pageInfo)
        {
            //使用正则表达式提取参数并替换为pageInfo对象的属性值,如果属性没有找到,则被替换为空字符串
            return PageMessageParamRegex.Replace(tpl, m =>
            {
                var proName = m.Value;
                if (m.Groups.Count > 1)
                {
                    proName = m.Groups[1].Value;
                }

                return ObjectHelper.GetObjectProperty(pageInfo, proName).ToStringOrEmpty();
            });
        }

        /// <summary>
        /// 修复当前页码,防止超过范围,如果控件已加载数据,那么当前页码不能超过最大页数
        /// </summary>
        /// <param name="currentPageIndex">当前页码</param>
        /// <param name="newPageSize">新的分页大小</param>
        /// <returns>返回正确的页码值</returns>
        private int RepairPageIndex(int currentPageIndex, int newPageSize)
        {
            if (currentPageInfo != null && currentPageInfo.Total > 0)
            {
                var tmpPageInfo = new PageInfo(currentPageIndex, newPageSize, currentPageInfo.Total);
                return tmpPageInfo.PageIndex;
            }

            return currentPageIndex;
        }
    }

    /// <summary>
    /// 分页控件设计器
    /// </summary>
    public class PaginationDesigner : System.Windows.Forms.Design.ControlDesigner
    {
        /// <summary>
        /// 不允许调整控件的高度
        /// </summary>
        public override SelectionRules SelectionRules => SelectionRules.Visible | SelectionRules.Moveable;
    }

    /// <summary>
    /// 分页事件参数对象
    /// </summary>
    public class PageChangedEventArgs : EventArgs
    {
        /// <summary>初始化 <see cref="PageChangedEventArgs" /> 类的新实例。</summary>
        /// <param name="pageIndex">页码索引</param>
        /// <param name="pageSize">每页大小</param>
        public PageChangedEventArgs(int pageIndex, int pageSize)
        {
            if (pageIndex < 1) pageIndex = 1;
            if (pageSize < 1) pageSize = 1;
            PageIndex = pageIndex;
            PageSize = pageSize;
        }

        /// <summary>
        /// 页码索引
        /// </summary>
        public int PageIndex { get; }

        /// <summary>
        /// 每页大小
        /// </summary>
        public int PageSize { get; }
    }
}